<script setup lang="ts">
import type { TableInstance } from 'element-plus'
import type { TableEvents, TableProps } from './index'

import { CirclePlus, DCaret, Delete, Rank, RefreshRight, Setting } from '@element-plus/icons-vue'
import { cloneDeep } from 'lodash'
import Sortable from 'sortablejs'
import { VueDraggable } from 'vue-draggable-plus'
import TableColumn from './tableColumn.vue'

const props = withDefaults(defineProps<TableProps>(), {
  tableLayout: 'auto',
  selectionChengeUse: 'id',
  showHeader: true,
  showPagination: true,
  isRadio: false,
  hideOnSinglePage: false,
  pagerCount: 7,
  fit: true,
  pageDisabled: false,
  pageLayout: 'total, sizes, prev, pager, next, jumper',
  hideDelete: false,
  hideAdd: false,
  clickRowSelect: true,
  pageSizes: () => {
    return [10, 20, 50, 100]
  },
  paging: () => {
    return {
      pageNum: 1,
      pageSize: 10,
      total: 200,
    }
  },
})

const emit = defineEmits<TableEvents>()
// column 列类型
const columnTypes = ['selection', 'index', 'expand', 'sort']
// 表格id
const tableId = useId()
// 表格ref
const tableRef = $ref<TableInstance>()
// 表格列数据
let columns = $ref(cloneDeep(props.columns))
const tableData = $computed(() => {
  return props.data ?? []
})
// 显示表格列数据
let showColumns = $ref(cloneDeep(props.columns))
// 表格列数据选择
let columnsCheckList = $ref<string[]>(props.columns.map(item => item.prop as string))
// 全选
let columnsCheckAll = $ref(true)
// 半选
let isIndeterminate = $ref(false)
// 多选数据
const multipleSelection = defineModel<any[]>('selection')
// 表格大小
const sizeOptions = [
  { label: '宽松', value: 'large' },
  { label: '默认', value: 'default' },
  { label: '紧凑', value: 'small' },
]
const size = $ref<'large' | 'default' | 'small'>('default')
// 表格刷新动画
let tableRefreshClass = $ref('')
// 插槽
const slots = useSlots()
const slotKeys = Object.keys(slots) as string[]

// 表格刷新动画
function tableRefresh() {
  tableRefreshClass = 'table-reload-click'
  setTimeout(() => {
    tableRefreshClass = ''
  }, 500)
  emit('refresh')
}
// 表格多选事件
function tableSelection(val: any[]) {
  let row: any[] = val
  if (props.isRadio) {
    if (val.length > 1) {
      tableRef?.toggleRowSelection(val.shift(), false)
      row = [val[0]]
    }
  }
  setSelectionData(row)
}

function setSelectionData(val: any[]) {
  if (props.selectionChengeUseDataFn) {
    multipleSelection.value = val.map(props.selectionChengeUseDataFn)
  }
  else if (props.selectionChengeUse === 'all') {
    multipleSelection.value = val
  }
  else if (props.selectionChengeUse === 'id') {
    multipleSelection.value = val.map(item => item.id)
  }
}
// 提供给外部调用更新多选数据
function updateSelection(val: any[]) {
  const data = props.data?.filter(item => val.includes(item[(props.rowKey ?? 'id') as any]))
  tableRef?.clearSelection()
  if (val.length === 0) {
    multipleSelection.value = []
    return
  }
  data?.forEach((item) => {
    tableRef?.toggleRowSelection(item, true)
  })
  tableSelection(data ?? [])
}

// 表格全选事件
function tableSelectionAll(val: any[]) {
  let row: any[] = val
  if (props.isRadio) {
    tableRef?.clearSelection()
    if (val.length > 1) {
      row = []
    }
  }
  tableSelection(row)
}

// 表格列全选事件
function columnsCheckAllChange(val: any) {
  if (val) {
    columnsCheckList = props.columns.map(item => item.prop as string)
  }
  else {
    columnsCheckList = []
  }
  // 过滤掉没有显示的表格列
  showColumns = columns.filter(item => columnsCheckList.includes(item.prop as string))
  isIndeterminate = false
}
// 重置显示表格列
function resetShowColumns() {
  columnsCheckList = props.columns.map(item => item.prop as string)
  showColumns = props.columns
  columns = props.columns
  columnsCheckAll = true
  isIndeterminate = false
}
// 表格列多选事件
function columnsCheckedCitiesChange(value: string[]) {
  const checkedCount = value.length
  showColumns = columns.filter(item => columnsCheckList.includes(item.prop as string))
  columnsCheckAll = columnsCheckList.length === columns.length
  isIndeterminate = checkedCount > 0 && checkedCount < props.columns.length
}

// 表格拖拽排序
function dragSort() {
  const tbody = document.querySelector(`#${tableId} tbody`) as HTMLElement
  Sortable.create(tbody, {
    handle: '.table-move',
    animation: 300,
    onEnd({ newIndex, oldIndex }) {
      if (!props.data)
        return
      const [removedItem] = tableData.splice(oldIndex!, 1)
      tableData.splice(newIndex!, 0, removedItem)
      emit('dragSort', newIndex as number, oldIndex as number, tableData)
    },
  })
}

// 分页变化事件
function paginationChange(pageNum: number, pageSize: number) {
  emit('pagingChange', pageNum, pageSize)
}
/**
 * 当单元格 hover 进入时会触发该事件
 */
function cellMouseEnter(row: any, column: any, cell: HTMLTableCellElement, event: Event) {
  emit('cellMouseEnter', row, column, cell, event)
}

/**
 * 当单元格 hover 进入时会触发该事件
 */
function cellMouseLeave(row: any, column: any, cell: HTMLTableCellElement, event: Event) {
  emit('cellMouseLeave', row, column, cell, event)
}

/**
 * 当单元格 当某个单元格被点击时会触发该事件
 */
function cellClick(row: any, column: any, cell: HTMLTableCellElement, event: Event) {
  emit('cellClick', row, column, cell, event)
}

/**
 * 当某个单元格被双击击时会触发该事件
 */
function celldbClick(row: any, column: any, cell: HTMLTableCellElement, event: Event) {
  emit('celldbClick', row, column, cell, event)
}

/**
 * 当某个单元格被鼠标右键点击时会触发该事件
 */
function cellContextmenu(row: any, column: any, cell: HTMLTableCellElement, event: Event) {
  emit('cellContextmenu', row, column, cell, event)
}

/**
 * 当某一行被点击时会触发该事件
 */
function rowClick(row: any, column: any, event: Event) {
  emit('rowClick', row, column, event)
  if (props.clickRowSelect) {
    updateSelection([row[(props.rowKey ?? 'id') as any]])
  }
}
/**
 * 当某一行被双击时会触发该事件
 */
function rowdbClick(row: any, column: any, event: Event) {
  emit('rowdbClick', row, column, event)
}

/**
 * 当某一行被鼠标右键点击时会触发该事件
 */
function rowContextmenu(row: any, column: any, event: Event) {
  emit('rowContextmenu', row, column, event)
}
/**
 * 当某一列的表头被点击时会触发该事件
 */
function headerClick(column: any, event: Event) {
  emit('headerClick', column, event)
}
/**
 * 当某一列的表头被鼠标右键点击时触发该事件
 */
function headerContextmenu(column: any, event: Event) {
  emit('headerContextmenu', column, event)
}

/**
 * 拖动表头改变了列的宽度的时候会触发该事件
 */
function headerDragend(newWidth: number, oldWidth: number, column: any, event: MouseEvent) {
  emit('headerDragend', newWidth, oldWidth, column, event)
}

/**
 * 当表格的排序条件发生变化的时候会触发该事件
 */
function sortChange(data: { column: any, prop: string, order: any }) {
  emit('sortChange', data)
}

/**
 * column 的 key， 如果需要使用 filter-change 事件，则需要此属性标识是哪个 column 的筛选条件
 */
function filterChange(newFilters: any) {
  emit('filterChange', newFilters)
}

/**
 * 当表格的当前行发生变化的时候会触发该事件，如果要高亮当前行，请打开表格的 highlight-current-row 属性
 */
function currentChange(currentRow: any, oldCurrentRow: any) {
  emit('currentChange', currentRow, oldCurrentRow)
}

/**
 * 当用户对某一行展开或者关闭的时候会触发该事件（展开行时，回调的第二个参数为 expandedRows；树形表格时第二参数为 expanded）
 */
function expandChange(row: any, expandedRows: any) {
  emit('expandChange', row, expandedRows)
}

/**
 * 表格被用户滚动后触发
 */
function tableScroll({ scrollLeft, scrollTop }: { scrollLeft: number, scrollTop: number }) {
  emit('tableScroll', scrollLeft, scrollTop)
}
/**
 * 点击新增按钮时触发
 */
function clickAdd() {
  emit('clickAdd')
}
/**
 * 点击删除按钮时触发
 */
function clickDel() {
  emit('clickDel')
}

function getTableRef() {
  return tableRef
}

// 监听columns变化
watch(() => columns, () => {
  // 过滤掉没有显示的列
  showColumns = columns.filter(item => columnsCheckList.includes(item.prop as string))
})

onMounted(() => {
  dragSort()
})

defineExpose({ getTableRef, updateSelection })
</script>

<template>
  <div style="overflow: hidden;" un-h-full>
    <div class="header">
      <div class="headerleft">
        <slot name="header" v-bind="{ size, disabled: multipleSelection?.length === 0 }" />
        <el-button
          v-if="!hideDelete"
          v-auth="`${deleteBtnPerms}`"
          type="danger"
          :size="size"
          :disabled="multipleSelection?.length === 0"
          plain
          @click="clickDel"
        >
          <template #icon>
            <el-icon>
              <Delete />
            </el-icon>
          </template>
          删除
        </el-button>
      </div>
      <div class="header-right">
        <div un-flex="~" un-m-r="10px">
          <el-button
            v-if="!hideAdd"
            v-auth="`${addBtnPerms}`"
            type="primary"
            :size="size"
            @click="clickAdd"
          >
            <template #icon>
              <el-icon>
                <CirclePlus />
              </el-icon>
            </template>
            新增
          </el-button>
        </div>
        <el-tooltip
          v-if="!hideTableSetting"
          content="刷新"
          placement="top"
          :size="size"
        >
          <div class="right-icon" :class="tableRefreshClass" @click="tableRefresh">
            <el-icon>
              <RefreshRight />
            </el-icon>
          </div>
        </el-tooltip>

        <el-divider v-if="!hideTableSetting" direction="vertical" />
        <el-dropdown
          v-if="!hideTableSetting"
          placement="bottom"
          trigger="click"
        >
          <div>
            <el-tooltip
              content="表格大小"
              placement="top"
            >
              <div class="right-icon">
                <el-icon>
                  <DCaret />
                </el-icon>
              </div>
            </el-tooltip>
          </div>
          <template #dropdown>
            <el-dropdown-menu>
              <el-dropdown-item
                v-for="item in sizeOptions"
                :key="item.value"
                :class="{ 'dropdown-menu-active': item.value === size }"
                @click="size = (item.value as any)"
              >
                {{ item.label }}
              </el-dropdown-item>
            </el-dropdown-menu>
          </template>
        </el-dropdown>
        <el-divider v-if="!hideTableSetting" direction="vertical" />
        <el-dropdown
          v-if="!hideTableSetting"
          placement="bottom-end"
          trigger="click"
        >
          <div>
            <el-tooltip
              content="列配置"
              placement="top"
            >
              <div class="right-icon">
                <el-icon>
                  <Setting />
                </el-icon>
              </div>
            </el-tooltip>
          </div>
          <template #dropdown>
            <div un-p="x-5 y-2" un-min-w="35">
              <div
                un-flex="~ justify-between items-center"
                un-m-b="4px"
                un-border-b="1px solid [var(--sy-layout-border-color)]"
              >
                <div un-flex="~ justify-between items-center gap-x-2">
                  <el-checkbox
                    v-model="columnsCheckAll"
                    :indeterminate="isIndeterminate"
                    @change="columnsCheckAllChange"
                  />
                  列显示
                </div>
                <span
                  un-cursor="pointer"
                  un-font-size="12px"
                  un-color="[var(--sy-color-primary)]"
                  @click="resetShowColumns"
                >重置</span>
              </div>
              <el-checkbox-group
                v-model="columnsCheckList"
                @change="columnsCheckedCitiesChange"
              >
                <VueDraggable v-model="columns">
                  <div v-for="item in columns" :key="item.prop">
                    <el-checkbox v-show="!item.fixed" :label="item.label" :value="item.prop" />
                  </div>
                </VueDraggable>
              </el-checkbox-group>
            </div>
          </template>
        </el-dropdown>
      </div>
    </div>
    <div
      :style="{
        height: props.showPagination ? (size === 'large' ? 'calc(100% - 143px)' : 'calc(100% - 130px)') : 'calc(100% - 85px)',
      }"
    >
      <el-table
        v-bind="{ ...props }"
        :id="tableId"
        ref="tableRef"
        v-loading="props.loading"
        :data="tableData"
        :size="size"
        @select="tableSelection"
        @select-all="tableSelectionAll"
        @cell-mouse-enter="cellMouseEnter"
        @cell-mouse-leave="cellMouseLeave"
        @cell-click="cellClick"
        @cell-dblclick="celldbClick"
        @cell-contextmenu="cellContextmenu"
        @row-click="rowClick"
        @row-dbclick="rowdbClick"
        @row-contextmenu="rowContextmenu"
        @header-click="headerClick"
        @header-contextmenu="headerContextmenu"
        @header-dragend="headerDragend"
        @sort-change="sortChange"
        @filter-change="filterChange"
        @current-change="currentChange"
        @expand-change="expandChange"
        @scroll="tableScroll"
      >
        <template v-for="(column) in showColumns" :key="column.prop">
          <el-table-column
            v-if="column.type && columnTypes.includes(column.type)"
            v-bind="{ ...column }"
          >
            <template v-if="column.type === 'expand'" #default="scope">
              <slot name="expand" v-bind="{ ...scope }" />
            </template>
            <template v-if="column.type === 'sort'">
              <el-icon class="table-move">
                <Rank />
              </el-icon>
            </template>
          </el-table-column>
          <TableColumn v-else :column="column">
            <template v-for="(item) in slotKeys" #[item]="scope">
              <slot :name="item" v-bind="scope" />
            </template>
          </TableColumn>
        </template>
        <!-- 当数据为空时自定义的内容 -->
        <template #empty>
          <slot name="empty">
            <el-empty />
          </slot>
        </template>
        <!-- 插入至表格最后一行之后的内容， 如果需要对表格的内容进行无限滚动操作，可能需要用到这个 slot。 若表格有合计行，该 slot 会位于合计行之上。 -->
        <template #append>
          <slot name="append" />
        </template>
      </el-table>
    </div>
    <div v-if="showPagination" class="pagination">
      <el-pagination
        background
        :hide-on-single-page="hideOnSinglePage"
        :pager-count="pagerCount"
        :disabled="pageDisabled"
        :size="size"
        :page-sizes="pageSizes"
        :default-page-size="paging.pageSize"
        :default-current-page="paging.pageNum"
        :layout="pageLayout"
        :total="props.paging.total"
        @change="paginationChange"
      />
    </div>
  </div>
</template>

<style scoped>
.header {
  display: flex;
  justify-content: space-between;
  margin-bottom: 15px;
  .header-right {
    display: flex;
    align-items: center;
    .right-icon {
      width: 30px;
      cursor: pointer;
      height: 100%;
      display: flex;
      align-items: center;
      justify-content: center;
      font-size: 16px;
    }
  }
}
.table-reload-click {
  animation: table-reload-an 0.5s ease-in-out;
}
.pagination {
  display: flex;
  justify-content: flex-end;
  margin-top: 15px;
}

:deep .sy-table__body-header th {
  background-color: var(--sy-fill-color-light) !important;
}

@keyframes table-reload-an {
  0% {
    transform: rotate(0deg);
  }
  100% {
    transform: rotate(360deg);
  }
}
:deep .sy-divider {
  margin: 0 5px;
}
</style>
